#pragma once
#include <iostream>
#include <string>
#include <memory>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "logmsg.pb.h"
#include "../../logs/log.h"

using namespace Net_Work;
using namespace EpollerModule;
// 采用Epoll来实现多个连接的监听
class EpollServer
{
    static const int gmaxevents = 64;

public:
    using ptr = std::shared_ptr<EpollServer>;
    EpollServer(int port) : _port(port),
                            _listen_socket(std::make_unique<TcpSocket>()),
                            _running(false),
                            _epoller(new Epoller())
    {
        InitServer();
    }

    // 入口
    void Loop()
    {
        _running = true;
        while (_running)
        {
            int timeout = -1;
            // 获取就绪的事件
            // 有n个文件描述符的事件就绪                               // 1S
            int n = _epoller->Wait(_revs, gmaxevents, timeout); // 方便测试，阻塞方案
            switch (n)
            {
            case 0:
                DEBUG("timeout...\n");
                break;
            case -1:
                ERROR("epoll wait failed!\n");
                break;
            default:
                // 事件就绪了
                INFO("event happend ... \n");
                HandlerEvent(n);
                break;
            }
        }
        _running = false;
    }

private:
    bool InitServer()
    {
        // 内核允许在等待连接队列中排队的最大连接数
        // 默认设置为5
        _listen_socket->BuildListenSocketMethod(_port, backlog);
        INFO("init socket success, listensock is : %d", _listen_socket->GetSockFd());

        // 创建epoll模型来进行等待（事件到来）
        _epoller->InitEpoll();
        INFO("init epoller success!", _listen_socket->GetSockFd());

        // 开始监听管理 _listen_socket
        _epoller->AddEvent(_listen_socket->GetSockFd(), EPOLLIN);

        return true;
    }
    bool Accepter(std::string *ip, uint16_t *port)
    {
        // 接收到一个新的fd，可通过这个fd对客户端进行通信
        // 和读写文件类似
        int n = _listen_socket->AcceptConnection(ip, port);
        if (n < 0)
        {
            WARN("accept error");
            return false;
        }
        // 将该连接的读事件添加进去
        _epoller->AddEvent(n, EPOLLIN);
        return true;
    }

    int Recver(int sockfd, std::string *out)
    {
        // 存放读取的数据
        char buffer[1024];
        int n = recv(sockfd, buffer, sizeof(buffer), 0); // 有bug:数据并不一定一次就读完了
        // recv不能循环读,epoll只能确保一次读就绪
        if (n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }
    void HandlerEvent(int n)
    {
        INFO("%d 个 events 准备就绪...", n);
        // 获取这些就绪事件进行处理
        for (int i = 0; i < n; i++)
        {
            // 获取文件描述符
            int sock_fd = _revs[i].data.fd;
            // 获取事件
            uint32_t events = _revs[i].events;
            if (events & EPOLLIN) // 读事件
            {
                // listen事件（有新连接到来
                if (sock_fd == _listen_socket->GetSockFd())
                {
                    std::string clientip;
                    uint16_t clientport;
                    // 建立连接, 并添加读事件
                    if (!Accepter(&clientip, &clientport))
                    {
                        continue;
                    }
                    INFO("accept client success, client[%s: %d]", clientip.c_str(), clientport);
                }
                else // 普通套接字的读事件（客户端发来消息）
                {
                    std::ofstream ofs;
                    std::string message;
                    // 返回读到了多少字节（最大1024字节）
                    int n = Recver(sock_fd, &message);
                    if (n > 0) // 对读到的数据进行处理（持久化输出到文件中）
                    {
                        // 得到客户端数据需要进行反序列化
                        log_msg::LogRequest req;
                        if (!req.ParseFromString(message))
                        {
                            std::cerr << "反序列化失败" << std::endl;
                            ofs.close();
                        }
                        // 然后进行存储
                        // ./UID/test-
                        // 创建目录
                        std::string pathname;
                        // ./UID/
                        pathname = "./" + req.hostid() + "/test.log";
                        logs::util::File::createDirectory(logs::util::File::getPath(pathname));
                        ofs.open(pathname, std::ios::app | std::ios::binary);
                        if(ofs.is_open()) ofs << req.msg();
                        else ERROR("文件打开失败！");
                        // std::cout << "client say# " << req.msg() << std::endl;
                    }
                    else
                    {
                        if (n == 0)
                        {
                            INFO("client %d close\n", sock_fd);
                        }
                        else
                        {
                            INFO("client recv %d error\n", sock_fd);
                        }
                        // epoll移除一个fd，这个fd必须是合法的fd，才能被正确移除
                        _epoller->DelEvent(sock_fd);
                        ::close(sock_fd);
                        ofs.close();
                    }
                }
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    std::unique_ptr<Epoller> _epoller;
    struct epoll_event _revs[gmaxevents];
    bool _running;
};